/*
 *  GameMeshes.cpp
 *  c_Render
 *
 *  Created by liao peterson on 11/8/09.
 *  Copyright 2009 uestc. All rights reserved.
 *
 */

#include "GameMeshes.h"

Mesh::Mesh()
{
	vertexCount		=0;
	triangleCount	=0;
	indexCount		=0;
	texWidth		=0;
	texHeight		=0;
	texEnable		=0;
	nameLength		=0;
	alpha			=0;
	frameNumber		=0;
	//loc				=(GLfloat*)malloc(sizeof(GLfloat)*3);
	//loc				=new vec3;
	dir				=(GLfloat*)malloc(sizeof(GLfloat)*3);
	size			=(GLfloat*)malloc(sizeof(GLfloat)*3);
	axis			=(GLfloat*)malloc(sizeof(GLfloat)*4);
	trans			=NULL;
	rot				=NULL;
	scale			=NULL;
	itrans			=NULL;
	irot			=NULL;
	iscale			=NULL;
	Model2World		=new Matrix4x4;
}

Mesh::~Mesh()
{
	for(int i=0;i<frameNumber;i++)
	{
		free(vertexArray[i]);
		free(normalArray[i]);
	}
	free(vertexArray);
	free(normalArray);
	free(currentVertex);
	free(currentNormal);
	free(uv);
	free(textureData);
	free(filename);
	//free(loc);
	//delete loc;
	free(size);
	free(axis);
	
#ifdef PHYSICS_MODE
	delete trans;
	delete rot;
	delete scale;
#endif
	
	delete quat;
	delete Model2World;
}

void Mesh::SetMatrix()
{
#ifdef PHYSICS_MODE
	if(!trans)
		trans = new Matrix4x4;
	if(!rot)
		rot = new Matrix4x4;
	if(!scale)
		scale = new Matrix4x4;
	if(!itrans)
		itrans = new Matrix4x4;
	if(!irot)
		irot = new Matrix4x4;
	if(!iscale)
		iscale = new Matrix4x4;
	
	trans->SetTranslate(loc.x, loc.y, loc.z);
	rot->SetRotQuat(quat);
	scale->SetScale(size[0], size[1], size[2]);
	Matrix4x4 matrix=(*trans)*(*rot)*(*scale);
	
	itrans->SetTranslate(-loc.x,-loc.y,-loc.z);
	irot->SetRotQuatI(quat);
	iscale->SetScale(1/size[0], 1/size[1], 1/size[2]);
#elif
	Matrix4x4 trans,rot,scale;
	trans.SetTranslate(loc.x, loc.y, loc.z);
	//rot.SetRotXYZ(-dir[0], -dir[1],-dir[2]);
	rot.SetRotQuat(quat);
	scale.SetScale(size[0], size[1], size[2]);
	Matrix4x4 matrix=trans*rot*scale;
#endif
	// Mark
	//if(Model2World)delete Model2World;
	Model2World->Copy4x4(&matrix);
}

void Mesh::SetMatrix(const GLfloat*pos,const GLfloat*ori,const GLfloat*scale)
{
	memcpy(&loc, pos, sizeof(GLfloat)*3);
	memcpy(dir, ori, sizeof(GLfloat)*3);
	memcpy(size,scale, sizeof(GLfloat)*3);
	
	SetMatrix();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Mesh::LoadTexture(GLchar*Name)
{
	CFStringRef path = CFStringCreateWithCString(NULL,Name,kCFStringEncodingUTF8);
	CFBundleRef	bundle = CFBundleGetMainBundle();
	CFURLRef fileURL = CFBundleCopyResourceURL(bundle, path,NULL, NULL);
	CGDataProviderRef provider = CGDataProviderCreateWithURL(fileURL);
	CGImageRef textureImage = CGImageCreateWithJPEGDataProvider(provider,
																NULL, 
																true, 
																kCGRenderingIntentDefault);
	CFRelease(path);
	CFRelease(fileURL);
	CGDataProviderRelease(provider);
	//CFRelease(bundle);
	if(textureImage==nil){
		perror("ERROR");
		printf("Failed to load texture image");
		return;
	}
	this->texWidth=CGImageGetWidth(textureImage);
	this->texHeight=CGImageGetHeight(textureImage);
	
	this->textureData=(GLubyte*)malloc(this->texWidth*this->texHeight*4);
	
	CGContextRef textureContext=CGBitmapContextCreate(this->textureData,
													  this->texWidth, 
													  this->texHeight, 
													  8, 
													  this->texWidth*4, 
													  CGImageGetColorSpace(textureImage),
													  kCGImageAlphaPremultipliedLast);
	assert(textureContext);
	CGContextDrawImage(textureContext,
					   CGRectMake(0.0, 0.0, (GLfloat)this->texWidth, (GLfloat)this->texHeight), 
					   textureImage);
	
	CGImageRelease(textureImage);
	CGContextRelease(textureContext);
	
}

void Meshes::LoadMeshes(char*filename)
{
	FILE*fp=fopen(filename,"r");
	if(fp==NULL)
		perror("Error");
	GLint number=0;
	fread(&number,sizeof(GLint),1,fp);
	fread(&frameNumber,sizeof(GLint),1,fp);
	frames=(GLint*)malloc(sizeof(GLint)*frameNumber);
	fread(frames,sizeof(GLint),frameNumber,fp);
	
	for(int i=0;i<number;i++)
	{
		Mesh*temp=new Mesh;
		temp->frameNumber=frameNumber;
		//temp->quat=new Quat;
		
		fread(&temp->vertexCount,sizeof(GLint),1,fp);
		fread(&temp->triangleCount, sizeof(GLint), 1, fp);
		fread(&temp->indexCount, sizeof(GLint), 1, fp);
		fread(&temp->texEnable,sizeof(GLint),1,fp);
		fread(&temp->loc,sizeof(GLfloat),3,fp);
		fread(temp->dir,sizeof(GLfloat),3,fp);
		fread(temp->size, sizeof(GLfloat), 3, fp);
		temp->SetMatrix();
		
		temp->vertexArray=(GLfloat**)malloc(sizeof(GLfloat*)*frameNumber);
		temp->normalArray=(GLfloat**)malloc(sizeof(GLfloat*)*frameNumber);
		for(int j=0;j<frameNumber;j++)
		{
			temp->vertexArray[j]=(GLfloat*)malloc(sizeof(GLfloat)*9*temp->triangleCount);
			temp->normalArray[j]=(GLfloat*)malloc(sizeof(GLfloat)*9*temp->triangleCount);
			fread(temp->vertexArray[j],sizeof(GLfloat),9*temp->triangleCount,fp);
			fread(temp->normalArray[j],sizeof(GLfloat),9*temp->triangleCount,fp);
		}
		
		temp->currentVertex=(GLfloat*)malloc(sizeof(GLfloat)*9*temp->triangleCount);
		temp->currentNormal=(GLfloat*)malloc(sizeof(GLfloat)*9*temp->triangleCount);
		memcpy(temp->currentVertex, temp->vertexArray[0], sizeof(GLfloat)*9*temp->triangleCount);
		memcpy(temp->currentNormal, temp->normalArray[0], sizeof(GLfloat)*9*temp->triangleCount);
		
		if(temp->texEnable)
		{
			fread(&temp->nameLength,sizeof(GLint),1,fp);
			temp->filename=(GLchar*)malloc(sizeof(GLchar)*temp->nameLength);
			fread(temp->filename,sizeof(GLchar),temp->nameLength,fp);
			temp->uv=(GLfloat*)malloc(sizeof(GLfloat)*6*temp->triangleCount);
			fread(temp->uv,sizeof(GLfloat),6*temp->triangleCount,fp);
		}
		mList->Append(temp);
	}
	
	textureIds=(GLuint*)malloc(sizeof(GLuint)*mList->count);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glGenTextures(mList->count, &textureIds[0]);
	Node<Mesh>*iterate=mList->head;
	for (int i=0; i<mList->count; i++) {
		iterate=iterate->next;
		iterate->data->LoadTexture(iterate->data->filename);
		glBindTexture(GL_TEXTURE_2D, textureIds[i]);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 
					 iterate->data->texWidth, iterate->data->texHeight,
					 0, GL_RGBA, GL_UNSIGNED_BYTE, iterate->data->textureData);
		glGenerateMipmap(GL_TEXTURE_2D);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
		//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	}
}

Meshes::Meshes()
{
	mList=new List<Mesh>;
	textureIds=NULL;
	frameNumber=0;
	frames=NULL;
	currentFrame=1;
	tick=0;
	this->fps=2;
}

Meshes::~Meshes()
{
	delete mList;
	free(textureIds);
	free(frames);
}

void Meshes::Animate()
{
	if(frameNumber==1)
		return;
	Node<Mesh>*node=mList->head->next;
	for(int i=0;i<mList->count;i++)
	{
		Mesh*mesh=node->data;
		int index=0;
		for(index;index<frameNumber;index++)
			if(frames[index]>currentFrame)
				break;
		GLfloat time=(GLfloat)(-currentFrame+frames[index])/(frames[index]-frames[index-1]);
		for(int j=0;j<node->data->triangleCount*9;j++)
		{
			mesh->currentVertex[j]=mesh->vertexArray[index-1][j]*time+mesh->vertexArray[index][j]*(1-time);
		}
		node=node->next;
	}
	tick++;
	if(tick>=1/fps)
	{
		tick=0;
		currentFrame++;
		if(currentFrame>=frames[frameNumber-1]-1)
			currentFrame=1;
	}
}
